home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Abalone 1.4.2 / src / Compute.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-09-21  |  19.3 KB  |  768 lines  |  [TEXT/MPS ]

  1. #define COMPUTE_C
  2. #include "Compute.h"
  3. #undef COMPUTE_C
  4.  
  5.  
  6. #pragma segment More
  7.  
  8.  
  9. //    There are several methods to find the best move to make.
  10. //    A general and simple case is FindBestBoard;
  11. //    FindBestMove is just a slightly more efficient special-case version.
  12. //    FindBestBoard iterates over all the valid moves,
  13. //    using a standard minimax strategy with a recursion level specified as in level,
  14. //    to find the move that gives the best (or next best) result.
  15. //    The easiest and most general way to implement a new strategy is to make a BoardJudgeProc.
  16. //    The function TheFinalStrategy right below is all yours to try this,
  17. //    but first read more about the conventions used in this program.
  18.  
  19. static unsigned char search_order [kFields+1] = 
  20. {
  21.      1,     2,     3,     4,     5,    11,    18,    26,    35,    43,    50,    56,    61, 60,    59,    58,    57,    51,    44,    36, 27,    19,    12,    6,
  22.     31,
  23.     22,    23,    32,    40,    39,    30,
  24.     14,    15,    16,    24,    33,    41,    48,    47,    46,    38,    29,    21,
  25.      7,     8,     9, 10,    17,    25,    34,    42,    49,    55,    54,    53,    52,    45,    37,    28,    20,    13,
  26.     0    //    0 = sentinel
  27. };
  28.  
  29. static unsigned char fliche_seek[kDirections][3] =
  30. {
  31.     {2, 3, 0},
  32.     {1, 3, 0},
  33.     {1, 2, 0},
  34.     {2, 3, 0},
  35.     {1, 3, 0},
  36.     {1, 2, 0}
  37. };
  38.  
  39.  
  40. static MoveData scratch;
  41.  
  42.  
  43. typedef struct _eval
  44. {
  45.     short        value;
  46.     MoveData    move;
  47.     
  48. }    Eval, *EvalPtr;
  49.  
  50.  
  51. static Eval        theEvals [16 * 6 * 3];    //    Size >= theoretical max. # of moves.
  52.  
  53.  
  54. short
  55. GenerateMoves (BoardPtr board, BoardJudgeProc judgeboard, short player, short level)
  56. {
  57.     Board        whatIfBoard;
  58.     MoveData     m;
  59.     short        index = 0;
  60.     FieldsPtr    fp;
  61.     
  62.     for (fp = search_order, m[0] = *fp; *fp; m[0] = *++fp)
  63.     {
  64.         if (board->field[m[0]] != player)
  65.             continue;
  66.             
  67.         for (m[3] = right; m[3] < down; m[3]++)
  68.         {
  69.             register unsigned char *fd;
  70.             register unsigned char    df;
  71.         
  72.             m[1] = empty;
  73.  
  74.             if (ValidMove (board, m))
  75.             {                
  76.                 CopyBoard (board, & whatIfBoard);
  77.                                                 
  78.                 (void) DoMove (& whatIfBoard, m);
  79.                 *((long *) theEvals[index].move) = *((long *) m);            
  80.                 theEvals[index++].value = (*judgeboard) (& whatIfBoard, player);
  81.             }
  82.  
  83. //            if (level == 0 && (gSet.Level[gTheGame.CurrentPlayer] % 1))
  84. //                continue;
  85.                 
  86.             for (fd = fliche_seek[m[3]], df = *fd; *fd; df = *fd++)
  87.             {                                                
  88.                 m[1] = Neighbour (m[0], df);
  89.              
  90.                 if (board->field[m[1]] != player)
  91.                     continue;
  92.             
  93.                 m[2] = empty;
  94.                             
  95. try_fliche_3_3:    if (ValidFlicheMoveSorted (board, m))
  96.                 {
  97.                     CopyBoard (board, & whatIfBoard);
  98.                                                     
  99.                     (void) DoFlicheMove (& whatIfBoard, m);
  100.                     
  101.                     *((long *) theEvals[index].move) = *((long *) m);            
  102.                     theEvals[index++].value = (*judgeboard) (& whatIfBoard, player);
  103.                 }
  104.  
  105.                 if (! m[2])
  106.                 {
  107.                     m[2] = Neighbour (m[1], df);
  108.                     
  109.                     if (board->field[m[2]] == player)    //    a fliche of length 3 in this direction
  110.                         goto try_fliche_3_3;
  111.                 }
  112.             }
  113.         }
  114.     }
  115.     return index;
  116. }
  117.  
  118.  
  119.  
  120. int
  121. CompareEvals (const void *e1, const void *e2)
  122. {
  123.     return *((short *) e2) - *((short *) e1);
  124. }
  125.  
  126.  
  127.  
  128. void
  129. SortMoves (short n_moves)
  130. {
  131.     qsort (theEvals, n_moves, sizeof (Eval), CompareEvals);
  132. }
  133.  
  134.  
  135.  
  136. short
  137. FindNiceBoard (BoardPtr board, BoardJudgeProc judgeboard, short player, MovePtr move, short level)
  138. {
  139. #    define                 SEARCH_WIDTH    5
  140.  
  141.     Board                whatIfBoard;
  142.     short                whatIfResult;
  143.     short                maxSoFar = kMinJudgeVal;
  144.     
  145.     short                n_moves;
  146.     register short        i;
  147.     Eval                tmpEval[SEARCH_WIDTH];
  148.     
  149.     if (gInterrupted)
  150.         return kMinJudgeVal;
  151.         
  152.     n_moves = GenerateMoves (board, judgeboard, player, level);    
  153.     
  154.     if (level <= 0)
  155.     {
  156.         register short    besti = 0;
  157.         
  158. find_nice_1:
  159.  
  160.         for (i = 0; i < n_moves; i++)
  161.             if (theEvals[i].value > theEvals[besti].value)
  162.                 besti = i;
  163.                 
  164.         if (MoveIsKO (theEvals[besti].move, 0, player))
  165.         {
  166.         //    The move we think nicest is a KO move, so we pick our second choise.
  167.         
  168.             theEvals[besti].value = kMinJudgeVal;
  169.             goto find_nice_1;
  170.         }
  171.             
  172.         * ((long *) move) = * ((long *) (theEvals[besti].move));
  173.         return theEvals[besti].value;
  174.     }
  175.  
  176.     qsort (theEvals, n_moves, sizeof (Eval), CompareEvals);
  177.  
  178. ////    Alternative special case level == 0    
  179. //    if (level <= 0)
  180. //    {
  181. //        * ((long *) move) = * ((long *) (theEvals[0].move));
  182. //        return theEvals[0].value;
  183. //    }
  184.         
  185.     memcpy (tmpEval, theEvals, SEARCH_WIDTH * sizeof (Eval));
  186.     
  187.     * ((long *) move) = 0L;    //    initialise (=invalidate) the move
  188.  
  189.     for (i = 0; i < SEARCH_WIDTH; i++)
  190.     {
  191.         SystemTime();
  192.             
  193.         if (gInterrupted)
  194.             return kMinJudgeVal;
  195.  
  196.         CopyBoard (board, & whatIfBoard);
  197.         
  198.         if (tmpEval[i].move[1])
  199.             (void) DoFlicheMove (& whatIfBoard, tmpEval[i].move);
  200.         else                    
  201.             (void) DoMove (& whatIfBoard, tmpEval[i].move);
  202.  
  203.         whatIfResult
  204.         =    - FindNiceBoard (    & whatIfBoard,
  205.                                 judgeboard,
  206.                                 NextPlayer (player),
  207.                                 scratch,
  208.                                 level - 1
  209.                             );
  210.                 
  211.         if (whatIfResult > maxSoFar)
  212.         {
  213.             if    (    ! (level == gSet.Level[gTheGame.CurrentPlayer]
  214.                     &&    MoveIsKO (tmpEval[i].move, level, player))
  215.                 )
  216.             {                    
  217.                 maxSoFar = whatIfResult;    //    best move so far!
  218.                 * ((long *) move) = * ((long *) (tmpEval[i].move));
  219.             }
  220.         }
  221.     }    
  222.     return maxSoFar;
  223. }
  224.  
  225.  
  226. //    The following finds the best move it can make, using a given BoardJudgeProc.
  227. //    You can use this unchanged for a standard MiniMax strategy with pruning,
  228. //    or you can modify it to use a different strategy, but you're on your own if you do this.
  229. //    If you do implement a new search method, you should also modify AutoPlay in Board.c.
  230.  
  231. short
  232. FindBestBoard (BoardPtr board, BoardJudgeProc judgeboard, short player, MovePtr move, short level, PruneJudgeProc prunemove, short alfa, short beta)
  233. {
  234.     MoveData             m;
  235.     register FieldsPtr    fp;
  236.  
  237.     Board                whatIfBoard;
  238.     short                whatIfResult;
  239.     short                minSoFar = kMaxJudgeVal;
  240.     short                maxSoFar = kMinJudgeVal;
  241.                                 
  242.     if (gInterrupted)
  243.         return kMinJudgeVal;
  244.     
  245.     * ((long *) move) = 0L;    //    initialise (=invalidate) the move
  246.     
  247. //    Generate all possible moves to find the best one.
  248. //    Field search order as specified in array search_order;
  249. //    contains what seems to be a profitable order for alpha-beta pruning.
  250.  
  251.     for (fp = search_order, m[0] = *fp; *fp; m[0] = *++fp)
  252.     {
  253.         if (board->field[m[0]] != player)    //    no ball to play on this field, try next field
  254.             continue;
  255.         
  256.         if (level == 1)
  257.             SystemTime();
  258.             
  259.         if (gInterrupted)
  260.             return kMinJudgeVal;
  261.         
  262.             
  263.     //    Field m[0] is owned by the current player.
  264.             
  265.         for (m[3] = right; m[3] < down; m[3]++)    //    Try moves in all directions
  266.         {    
  267.             register unsigned char     *fd;
  268.             register unsigned char     df;
  269.             
  270.         //    Straight moves go first.
  271.         //    (Straight moves are generally more profitable than fliche moves.)
  272.             
  273.             m[1] = 0;    //    Show this is not a fliche move
  274.  
  275.             if (ValidMove (board, m))
  276.             {                
  277.                 CopyBoard (board, & whatIfBoard);
  278.                                                 
  279.                 (void) DoMove (& whatIfBoard, m);
  280.                 
  281.                 if (level <= 0 || prunemove != NULL)
  282.                 {
  283.                 //    Need to compute the judgement of this board,
  284.                 //    either as a parameter for the pruning function or as the final result.
  285.                 
  286.                     whatIfResult = (*judgeboard) (& whatIfBoard, player);
  287.                 }
  288.                 
  289.                 if    (    level > 0
  290.                     ||    prunemove == NULL
  291.                     ||    !((*prunemove) (level, whatIfResult, maxSoFar))
  292.                     )
  293.                 
  294.                     whatIfResult = (level <= 0)
  295.                         ?    (*judgeboard) (& whatIfBoard, player)
  296.                         :     - FindBestBoard (    & whatIfBoard,
  297.                                                 judgeboard,
  298.                                                 NextPlayer (player),
  299.                                                 scratch,
  300.                                                 level - 1,
  301.                                                 prunemove,
  302.                                                 alfa,
  303.                                                 beta
  304.                                             );
  305.                 
  306.                 if (whatIfResult > maxSoFar)
  307.                 {
  308.                     if (! (level == gSet.Level[gTheGame.CurrentPlayer] && MoveIsKO (m, level, player)))
  309.                     {                    
  310.                         if (whatIfResult > maxSoFar)    //    best move so far!
  311.                             * ((long *) move) = * ((long *) (& m));
  312.                             
  313.                         if (AlfaBeta (player, whatIfResult, &minSoFar, &maxSoFar, &alfa, &beta))
  314.                             return player == gTheGame.CurrentPlayer ? minSoFar : maxSoFar;
  315.                     }
  316.                 }
  317.             }
  318.         //    Now the fliche moves.
  319.             
  320.         //    m[3]    ::= the direction the fliche moves to
  321.         //    df        ::= the direction of the fliche itself.
  322.         //    only the first 3 directions need to be tested for df
  323.                 
  324.             for (fd = fliche_seek[m[3]], df = *fd; *fd; df = *fd++)
  325.             {
  326.             //    For the first three directions that are not aligned with the fliche...
  327.                             
  328.                 m[1] = Neighbour (m[0], df);
  329.             
  330.             //    it is OK to speak about fliche moves of length 1,
  331.             //    but these are just the same as ordinary moves and are handled above.
  332.              
  333.                 if (board->field[m[1]] != player)    //    no fliche of length 2 in this direction...
  334.                     continue;                        //    ...so there won't be one of length 3 either
  335.             
  336.             //    a fliche of lenght 2 exists.
  337.             //    now test if we can move it (in direction m[3]), and what this would bring us
  338.                             
  339.                 m[2] = empty;
  340.                             
  341. try_fliche_3_2:    if (ValidFlicheMoveSorted (board, m))
  342.                 {
  343.                     CopyBoard (board, & whatIfBoard);
  344.                                                     
  345.                     (void) DoFlicheMove (& whatIfBoard, m);
  346.                     
  347.                     if (level <= 0 || prunemove != NULL)
  348.                     
  349.                         whatIfResult = (*judgeboard) (& whatIfBoard, player);
  350.                     
  351.                     if    (    level > 0
  352.                         ||    prunemove == NULL
  353.                         ||    !((*prunemove) (level, whatIfResult, maxSoFar))
  354.                         )
  355.                     
  356.                         whatIfResult = (level <= 0)
  357.                             ?    (*judgeboard) (& whatIfBoard, player)
  358.                             :     - FindBestBoard (    & whatIfBoard,
  359.                                                     judgeboard,
  360.                                                     NextPlayer (player),
  361.                                                     scratch,
  362.                                                     level - 1,
  363.                                                     prunemove,
  364.                                                     alfa,
  365.                                                     beta
  366.                                                 );
  367.                                                 
  368.                     if (whatIfResult > maxSoFar)
  369.                     {
  370.                         if (! (level == gSet.Level[gTheGame.CurrentPlayer] && MoveIsKO (m, level, player)))
  371.                         {                    
  372.                             if (whatIfResult > maxSoFar)    //    best move so far!
  373.                                 * ((long *) move) = * ((long *) (& m));
  374.                                 
  375.                             if (AlfaBeta (player, whatIfResult, &minSoFar, &maxSoFar, &alfa, &beta))
  376.                                 return player == gTheGame.CurrentPlayer ? minSoFar : maxSoFar;
  377.                         }
  378.                     }
  379.                                     
  380.                 }
  381.  
  382.             //    if there is a third ball for this fliche, try the fliche of length 3 as well
  383.                 
  384.                 if (! m[2])        //    Wait a minute! Didn't we just do this ?
  385.                 {
  386.                     m[2] = Neighbour (m[1], df);
  387.                     
  388.                     if (board->field[m[2]] == player)    //    a fliche of length 3 in this direction
  389.                         goto try_fliche_3_2;
  390.                                     
  391.                 }
  392.             }
  393.         }
  394.     }
  395.     return maxSoFar;
  396. }
  397.  
  398.  
  399.  
  400.  
  401. //    Same as FindBestBoard, but with some clever (?) search ordering
  402. //    and with lowest-level fliche moves all pruned out.
  403.  
  404. short
  405. FindGoodBoard (BoardPtr board, BoardJudgeProc judgeboard, short player, MovePtr move, short level, PruneJudgeProc prunemove, short alfa, short beta)
  406. {
  407.     MoveData             m;
  408.     register FieldsPtr    fp;
  409.     register short         df;
  410.  
  411.     Board                whatIfBoard;
  412.     short                whatIfResult;
  413.     short                minSoFar = kMaxJudgeVal;
  414.     short                maxSoFar = kMinJudgeVal;
  415.     
  416.     if (gInterrupted)
  417.         return kMinJudgeVal;
  418.     
  419.     * ((long *) move) = 0L;    //    initialise (=invalidate) the move
  420.  
  421.     for (fp = search_order, m[0] = *fp; *fp; m[0] = *++fp)
  422.     {
  423.         if (board->field[m[0]] != player)    //    no ball to play on this field, try next field
  424.             continue;
  425.         
  426.         if (level == 1)
  427.             SystemTime();
  428.             
  429.         if (gInterrupted)
  430.             return kMinJudgeVal;
  431.             
  432.     //    Field m[0] is owned by the current player
  433.             
  434.         for (m[3] = right; m[3] < down; m[3]++)    //    Try moves in all directions
  435.         {    
  436.         //    Straight moves go first.
  437.             
  438.             m[1] = 0;    //    Show this is not a fliche move.
  439.  
  440.             if (ValidMove (board, m))
  441.             {                
  442.                 CopyBoard (board, & whatIfBoard);
  443.                                                 
  444.                 (void) DoMove (& whatIfBoard, m);
  445.                 
  446.                 if (level <= 0 || prunemove != NULL)
  447.                 
  448.                     whatIfResult = (*judgeboard) (& whatIfBoard, player);
  449.                 
  450.                 
  451.                 if    (    level > 0
  452.                     ||    prunemove == NULL
  453.                     ||    !((*prunemove) (level, whatIfResult, maxSoFar))
  454.                     )
  455.                 
  456.                     whatIfResult = (level <= 0)
  457.                         ?    (*judgeboard) (& whatIfBoard, player)
  458.                         :     - FindGoodBoard (    & whatIfBoard,
  459.                                                 judgeboard,
  460.                                                 NextPlayer (player),
  461.                                                 scratch,
  462.                                                 level - 1,
  463.                                                 prunemove,
  464.                                                 alfa,
  465.                                                 beta
  466.                                             );
  467.                 
  468.                 if (whatIfResult > maxSoFar)
  469.                 {
  470.                     if (! (level == gSet.Level[gTheGame.CurrentPlayer] && MoveIsKO (m, level, player)))
  471.                     {                    
  472.                         if (whatIfResult > maxSoFar)    //    best move so far!
  473.                             * ((long *) move) = * ((long *) (& m));
  474.                             
  475.                         if (AlfaBeta (player, whatIfResult, &minSoFar, &maxSoFar, &alfa, &beta))
  476.                             return player == gTheGame.CurrentPlayer ? minSoFar : maxSoFar;
  477.                     }
  478.                 }
  479.             }
  480.             
  481.             if (level == 0 && (gSet.Level[gTheGame.CurrentPlayer] % 1))
  482.             
  483.             //    Skip all fliche moves if we move last! Now that's pruning! 
  484.                 
  485.                 continue;
  486.             
  487.         //    And now the fliche moves.
  488.             
  489.         //    m[3]    ::= the direction the fliche moves to
  490.         //    df        ::= the direction of the fliche itself
  491.         //    only the first 3 directions need to be tested for df
  492.                 
  493.             for (df = right; df < left; df++)
  494.             {                                    
  495.             //    skip 'fliche' moves along axis of fliche:
  496.             //    these are ordinary moves and are handled above.
  497.                 
  498.                 if (m[3] == df || m[3] == df + 3)    //    along axis of fliche
  499.                     continue;
  500.             
  501.                 m[1] = Neighbour (m[0], df);
  502.             
  503.             //    it is OK to speak about fliche moves of length 1,
  504.             //    but these are just the same as ordinary moves and are handled above.
  505.              
  506.                 if (board->field[m[1]] != player)    //    no fliche of length 2 in this direction...
  507.                     continue;                        //    ...so there won't be one of length 3 either
  508.             
  509.             //    A fliche of lenght 2 exists.
  510.             //    now test if we can move it (in direction m[3]), and what this would bring us.
  511.                             
  512.                 m[2] = empty;
  513.                             
  514. try_fliche_3_1:    if (ValidFlicheMoveSorted (board, m))
  515.                 {
  516.                     CopyBoard (board, & whatIfBoard);
  517.                                                     
  518.                     (void) DoFlicheMove (& whatIfBoard, m);
  519.                     
  520.                     if (level <= 0 || prunemove != NULL)
  521.                     
  522.                         whatIfResult = (*judgeboard) (& whatIfBoard, player);
  523.                     
  524.                     if    (    level > 0
  525.                         ||    prunemove == NULL
  526.                         ||    !((*prunemove) (level, whatIfResult, maxSoFar))
  527.                         )
  528.                     
  529.                         whatIfResult = (level <= 0)
  530.                             ?    (*judgeboard) (& whatIfBoard, player)
  531.                             :     - FindGoodBoard (    & whatIfBoard,
  532.                                                     judgeboard,
  533.                                                     NextPlayer (player),
  534.                                                     scratch,
  535.                                                     level - 1,
  536.                                                     prunemove,
  537.                                                     alfa,
  538.                                                     beta
  539.                                                 );
  540.                     
  541.                     if (whatIfResult > maxSoFar)
  542.                     {
  543.                         if (! (level == gSet.Level[gTheGame.CurrentPlayer] && MoveIsKO (m, level, player)))
  544.                         {                    
  545.                             if (whatIfResult > maxSoFar)    //    best move so far!
  546.                                 * ((long *) move) = * ((long *) (& m));
  547.                                 
  548.                             if (AlfaBeta (player, whatIfResult, &minSoFar, &maxSoFar, &alfa, &beta))
  549.                                 return player == gTheGame.CurrentPlayer ? minSoFar : maxSoFar;
  550.                         }
  551.                     }
  552.                 }
  553.  
  554.             //    if there is a third ball for this fliche, try the fliche of length 3 as well
  555.  
  556.                 if (! m[2])        //    Wait a minute! Didn't we just do this ?
  557.                 {
  558.                     m[2] = Neighbour (m[1], df);
  559.                     
  560.                     if (board->field[m[2]] == player)    //    a fliche of length 3 in this direction
  561.                         goto try_fliche_3_1;
  562.                                     
  563.                 }
  564.             }
  565.         }
  566.     }
  567.     return maxSoFar;
  568. }
  569.  
  570.  
  571.  
  572. short
  573. FindBestMove (BoardPtr board, MoveJudgeProc judgemove, short player, MovePtr move, short level, short alfa, short beta)
  574. {
  575.     MoveData            m;
  576.     register short        df;
  577.     short                minSoFar = kMaxJudgeVal;
  578.     short                maxSoFar = kMinJudgeVal;
  579.  
  580.     Board                whatIfBoard;
  581.     short                whatIfResult;
  582.         
  583.     * ((long *) move) = 0L;
  584.     
  585.     for (m[0] = 1; m[0] <= kFields; m[0]++)
  586.     {
  587.         if (board->field[m[0]] != player)
  588.             continue;
  589.         
  590.         if (level == 1)
  591.             SystemTime();
  592.             
  593.         for (m[3] = right; m[3] < down; m[3]++)
  594.         {    
  595.             m[1] = 0;
  596.  
  597.             if (ValidMove (board, m))
  598.             {
  599.                 whatIfResult = (*judgemove) (board, player, m);
  600.                 if (level > 0)
  601.                 {
  602.                     CopyBoard (board, & whatIfBoard);
  603.                     (void) DoMove (& whatIfBoard, m);
  604.                     whatIfResult -= FindBestMove (    & whatIfBoard,
  605.                                                         judgemove,
  606.                                                         NextPlayer (player),
  607.                                                         scratch,
  608.                                                         level - 1,
  609.                                                         player == gTheGame.CurrentPlayer ? maxSoFar : alfa,
  610.                                                         player == gTheGame.CurrentPlayer ? beta : minSoFar
  611.                                                     );
  612.                 }
  613.                 if (whatIfResult > maxSoFar)
  614.                 {
  615.                     if (! (level == gSet.Level[gTheGame.CurrentPlayer] && MoveIsKO (m, level, player)))
  616.                     {                    
  617.                         if (whatIfResult > maxSoFar)    //    best move so far!
  618.                             * ((long *) move) = * ((long *) (& m));
  619.                             
  620.                         if (AlfaBeta (player, whatIfResult, &minSoFar, &maxSoFar, &alfa, &beta))
  621.                             return player == gTheGame.CurrentPlayer ? minSoFar : maxSoFar;
  622.                     }
  623.                 }
  624.             }
  625.                 
  626.             for (df = right; df < left; df++)
  627.             {                                    
  628.                 if (m[3] == df || m[3] == df + 3)
  629.                     continue;
  630.             
  631.                 m[1] = Neighbour (m[0], df);
  632.              
  633.                 if (board->field[m[1]] != player)
  634.                     continue;
  635.                             
  636.                 m[2] = empty;
  637.                             
  638.                 if (ValidFlicheMoveSorted (board, m))
  639.                 {
  640.                     whatIfResult = (*judgemove) (board, player, m);
  641.                     if (level > 0)
  642.                     {
  643.                         CopyBoard (board, & whatIfBoard);
  644.                         (void) DoFlicheMove (& whatIfBoard, m);
  645.                         whatIfResult -= FindBestMove (    & whatIfBoard,
  646.                                                             judgemove,
  647.                                                             NextPlayer (player),
  648.                                                             scratch,
  649.                                                             level - 1,
  650.                                                             player == gTheGame.CurrentPlayer ? maxSoFar : alfa,
  651.                                                             player == gTheGame.CurrentPlayer ? beta : minSoFar
  652.                                                         );
  653.                     }                    
  654.                     if (whatIfResult > maxSoFar)
  655.                     {
  656.                         if (! (level == gSet.Level[gTheGame.CurrentPlayer] && MoveIsKO (m, level, player)))
  657.                         {                    
  658.                             if (whatIfResult > maxSoFar)
  659.                                 * ((long *) move) = * ((long *) (& m));
  660.                                 
  661.                             if (AlfaBeta (player, whatIfResult, &minSoFar, &maxSoFar, &alfa, &beta))
  662.                                 return player == gTheGame.CurrentPlayer ? minSoFar : maxSoFar;
  663.                         }
  664.                     }
  665.                 }
  666.  
  667.                 m[2] = Neighbour (m[1], df);
  668.             
  669.                 if (board->field[m[2]] != player)
  670.                     continue;
  671.                                 
  672.                 if (ValidFlicheMoveSorted (board, m))
  673.                 {
  674.                     whatIfResult = (*judgemove) (board, player, m);
  675.                     if (level > 0)
  676.                     {
  677.                         CopyBoard (board, & whatIfBoard);
  678.                         (void) DoFlicheMove (& whatIfBoard, m);
  679.                         whatIfResult -= FindBestMove (    & whatIfBoard,
  680.                                                             judgemove,
  681.                                                             NextPlayer (player),
  682.                                                             scratch,
  683.                                                             level - 1,
  684.                                                             player == gTheGame.CurrentPlayer ? maxSoFar : alfa,
  685.                                                             player == gTheGame.CurrentPlayer ? beta : minSoFar
  686.                                                         );
  687.                     }                    
  688.                     if (whatIfResult > maxSoFar)
  689.                     {
  690.                         if (! (level == gSet.Level[gTheGame.CurrentPlayer] && MoveIsKO (m, level, player)))
  691.                         {                    
  692.                             if (whatIfResult > maxSoFar)
  693.                                 * ((long *) move) = * ((long *) (& m));
  694.                                 
  695.                             if (AlfaBeta (player, whatIfResult, &minSoFar, &maxSoFar, &alfa, &beta))
  696.                                 return player == gTheGame.CurrentPlayer ? minSoFar : maxSoFar;
  697.                         }
  698.                     }
  699.                 }
  700.             }
  701.         }    
  702.     }
  703.     return maxSoFar;
  704. }
  705.  
  706.  
  707.  
  708. Boolean
  709. AlfaBeta (short player, short value, short *min, short *max, short *alfa, short *beta)
  710. {
  711.     if (value > *max)
  712.         *max = value;
  713.  
  714.     if (value < *min)
  715.         *min = value;
  716.         
  717.     if (player == gTheGame.CurrentPlayer)
  718.     {
  719.         if (*max > *alfa)
  720.             *alfa = *max;
  721.         
  722.         return (*max > *beta);
  723.     }
  724.     else
  725.     {
  726.         if (*min < *beta)
  727.             *beta = *min;
  728.         
  729.         return (*min < *alfa);
  730.     }
  731. }
  732.  
  733. //static unsigned char spiral_in [kFields+1] = 
  734. //{
  735. //    1,    2,    3,    4,    5,    11,    18,    26,    35,    43,    50,    56,    61,
  736. //    60,    59,    58,    57,    51,    44,    36,    27,    19,    12,    6,    7,    8,    9,
  737. //    10,    17,    25,    34,    42,    49,    55,    54,    53,    52,    45,    37,    28,    20,
  738. //    13,    14,    15,    16,    24,    33,    41,    48,    47,    46,    38,    29,    21,    22,
  739. //    23,    32,    40,    39,    30,    31, 0    //    0 = sentinel
  740. //};
  741.  
  742. //static Field spiral_out [kFields+1] = 
  743. //{
  744. //    31, 30, 39, 40, 32, 23, 22, 21, 29, 38, 46, 47, 48,
  745. //    41, 33, 24, 16, 15, 14, 13, 20, 28, 37, 45, 52, 53, 54,
  746. //    55, 49, 42, 34, 25, 17, 10,  9,  8,  7,  6, 12, 19, 27,
  747. //    36, 44, 51, 57, 58, 59, 60, 61, 56, 50, 43, 35, 26, 18,
  748. //    11,  5,  4,  3,  2,  1, 0    //    0 = sentinel
  749. //};
  750.  
  751.  
  752. //Boolean
  753. //PlayerHasBallOnOuterRing (BoardPtr board, short player)
  754. //{
  755. //    static Field outer_ring [25] = 
  756. //    {
  757. //        1,    2,    3,    4,    5,    11,    18,    26,    35,    43,    50,    56,    61,
  758. //        60,    59,    58,    57,    51,    44,    36,    27,    19,    12,    6, 0
  759. //    };
  760. //    register FieldsPtr    fp, rp;
  761. //    
  762. //    for (fp = board->field, rp = outer_ring; *rp; rp++)
  763. //        if (*(fp + *rp) == player)
  764. //            return true;
  765. //
  766. //    return false;
  767. //}
  768.